Summary of Behavior

When you create an Optional from a mutable or immutable object, the Optional holds a reference to that object in memory. Reassigning the original local variable to null does not affect the value stored inside the Optional.

Code Example

public class App {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("test");
        Optional<StringBuilder> sOp = Optional.of(sb);

        sb = null; // Reassigns local variable, but sOp is untouched

        if (sOp.isPresent()) {
            System.out.println("sOp is not null"); // Prints this
        } else {
            System.out.println("sOp is null");
        }
        
        sOp.ifPresent(System.out::println); // Prints "test"
    }
}

Output

sOp is not null
test

Modern Ways to Handle Optional in JDK 21

Since you are using JDK 21, you have access to clean, expressive patterns to process and display Optional values without messy if-else blocks.

1. Using Stream API (Optional.stream())

Since Java 9 (and fully supported in JDK 21), Optional provides a .stream() method. This allows you to treat an Optional as a Stream of 0 or 1 element, making it easy to chain with other stream operations like .map() or .filter().

import java.util.Optional;

public class StreamExample {
    public static void main(String[] args) {
        Optional<StringBuilder> sOp = Optional.of(new StringBuilder("test"));

        sOp.stream()
           .map(StringBuilder::toString)
           .map(String::toUpperCase)
           .forEach(System.out::println); // Prints: TEST
    }
}

Why use .stream()? It is extremely useful when you want to combine multiple Optional objects or filter them inside a larger stream pipeline without unpacking them manually.

2. Pattern Matching for switch (JDK 21 Feature Preview/Finalized)

While Optional doesn’t directly pattern-match in a switch expression out of the box, you can combine modern Java features or use newer functional idioms available in JDK 21.

However, for standard conditional checks, modern functional methods on Optional remain the cleanest approach:

// Executes only if present, otherwise runs the alternative
sOp.ifPresentOrElse(
    val -> System.out.println("Found: " + val),
    () -> System.out.println("Value is missing")
);

3. Transforming and Filtering (map and filter)

If you want to conditionally process the contents only if they meet a condition:

sOp.filter(val -> val.length() > 2)
   .ifPresent(val -> System.out.println("Valid length: " + val));

Best Practice with Optional Avoid using Optional.get() or checking isPresent() followed by get(). Instead, prefer functional methods like ifPresent, map, orElse, or ifPresentOrElse to write safer, more idiomatic Java code.